﻿using System;
using System.Xml;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;

namespace ESLab1
{
    class RuleExec
    {
        private string fileName;
        XmlDocument xmlDoc;

        Stack<Variable> goalVars = new Stack<Variable>();
        List<Variable> variables = new List<Variable>();

        List<Rule> activatedRules = new List<Rule>();
        IEnumerator ruleIterator;

        public static Rule lastRule;
        Main form;

        public RuleExec(string fileName, Main form)
        {
            this.fileName = fileName;
            xmlDoc = new XmlDocument();
            xmlDoc.Load(fileName);
            this.form = form;
        }

        public void GetAllVariblesInBZ()
        {
            XmlNodeList var = xmlDoc.GetElementsByTagName("var");

            //XmlNode firstRule = rules.Item(0);
            string currentGoal = string.Empty;
            IEnumerator ienum = var.GetEnumerator();

            XmlNode varXml = (XmlNode)ienum.Current;

            while (ienum.MoveNext())
            {
                XmlNode ruleXml = (XmlNode)ienum.Current;
                variables.Add(GetVaribleInDefinition(ruleXml));
            }

        }

		public IEnumerator GetAllRulesInBZ()
		{
			XmlNodeList var = xmlDoc.GetElementsByTagName("rule");

			//XmlNode firstRule = rules.Item(0);
			string currentGoal = string.Empty;
			IEnumerator ienum = var.GetEnumerator();
			return ienum;
		}

        public Variable GetVaribleInDefinition(XmlNode var)
        {
            Dictionary<string, string> par = new Dictionary<string, string>();
            foreach (XmlNode param in var.ChildNodes)
            {

                par.Add(param.Name, param.InnerText);
            }
            return new Variable(par["NAME"], par["TYPE"], par["COMMENT"], Convert.ToBoolean(par["NEEDED"]));
        }

        public Variable GetGoal()
        {
            XmlNodeList var = xmlDoc.GetElementsByTagName("goal");

            IEnumerator ienum = var.GetEnumerator();
            ienum.MoveNext();

            XmlNode varXml = (XmlNode)ienum.Current;
            return GetVaribleInDefinition(varXml);
        }

        public void GetRuleIterator()
        {
            XmlNodeList rules = xmlDoc.GetElementsByTagName("rule");
            ruleIterator =  rules.GetEnumerator();
        }

        public Rule getRuleByResult(Variable currentGoal)
        {
            string goalName = currentGoal.Name;

            bool notFind = true;

            while (ruleIterator.MoveNext() && notFind)
            {
                XmlNode ruleXml = (XmlNode)ruleIterator.Current;
                Rule rule = getRule(ruleXml);
                if (rule.Result.Name == goalName)
                    return rule;
            }

            return null;

        }

 
        public Rule getRule(XmlNode ruleXml)
        {
            Rule rule = new Rule();
           // rule.Name = ruleXml.Name;
            foreach (XmlAttribute attr in ruleXml.Attributes)
            {
                rule.Name = attr.Value;
            }
            
            IEnumerator ruleEnumerator = ruleXml.GetEnumerator();
            ruleEnumerator.MoveNext();

            XmlNode ifNode = (XmlNode)ruleEnumerator.Current;
            rule.Sender = getVariables(ifNode);

            ruleEnumerator.MoveNext();
            XmlNode thenNode = (XmlNode)ruleEnumerator.Current;
            rule.Result = getVariables(thenNode)[0];
            //while (ienum.MoveNext())
            //{
            //    XmlNode title = (XmlNode)ienum.Current;
            //    Console.WriteLine(title.InnerText);
            //foreach (XmlNode ch in table.ChildNodes)
            //{

            //}
            return rule;
        }

       
        public Error BackwardChaining()
        {
            GetAllVariblesInBZ();
            GetRuleIterator();
            goalVars.Push(GetGoal());

            if (goalVars.Count > 0)
            {
                Rule rule = getRuleByResult(goalVars.Peek());
                activatedRules.Add(rule);

                while (rule != null)
                {
                    int i=0;
                    bool ruleTrue = true;
                    IEnumerator ienum = rule.Sender.GetEnumerator();
                    while((ienum.MoveNext()) && (ruleTrue))
               //     foreach (var perem in rule.Sender.)
                    {
                        Variable perem = (Variable)ienum.Current;
                        Variable var = FindPeremByName(perem.Name);
                        if (var.Value == null)
                        {
                            if (var.Needed)
                            {
                                if (ShowWindow(var.Comment+"?"))
                                    FindPeremByName(perem.Name).Value = true;
                                else
                                    FindPeremByName(perem.Name).Value = false;

                                ruleTrue =  CheckCon(perem, var);
                            }
                            else
                            {
                                goalVars.Push(perem);
                                rule = getRuleByResult(goalVars.Peek());
                                if (rule == null)
                                {
                                    MessageBox.Show("Правило не найдено");
                                    break;
                                }
                                activatedRules.Add(rule);
                            }

                        }
                        else
                        {
                            ruleTrue = CheckCon(perem, var);
                        }
                        if (!ruleTrue)
                        {
                            rule = getRuleByResult(goalVars.Peek());
                            if (rule == null)
                            {
                                MessageBox.Show("Правило не найдено");
                                break;
                            }
                            activatedRules.Add(rule);
                        }

                    }
                    if (!ruleTrue)
                    {
                       // rule = getRuleByResult(goalVars.Peek());
                        activatedRules.Add(rule);
                    }
                    else
                    {
                        MessageBox.Show("Найдено решение");
                        ShowExplanation(rule);
                        lastRule = rule;
                        form.просмотрРезультатаToolStripMenuItem.Enabled = true;
                        rule = null;
                    }

                }
                if (rule == null)
                {
                    return new Error(false, "Правил, содержащих целевую переменную не найдено. Система несовместна");
                }
            }
            else
            {
                return new Error(true, string.Empty);
            }

            return new Error(true, string.Empty);

        }

		public void ForwardChaining()
		{
			IEnumerator ienumRules = GetAllRulesInBZ();
			ienumRules.MoveNext();
			goalVars.Push(GetGoal());
            int i = 0;
			bool isCompatible = true;
			if ((i < variables.Count) && (isCompatible))
			{
				XmlNode r = (XmlNode) ienumRules.Current;
				Rule rule = getRule(r);
				i = 0;
				while ((rule != null) && (isCompatible))
				{
					bool ruleTrue = true;
					IEnumerator ienum = rule.Sender.GetEnumerator();
					while ((ienum.MoveNext()) && (ruleTrue))
					{
						Variable perem = (Variable)ienum.Current;
						Variable var = FindPeremByName(perem.Name);
						if (var.Value == null)
						{
							if (var.Needed)
							{
								if (ShowWindow(var.Comment + "?"))
									FindPeremByName(perem.Name).Value = true;
								else
									FindPeremByName(perem.Name).Value = false;

								ruleTrue = CheckCon(perem, var);
							}
							else
							{
								goalVars.Push(perem);
								if (ienumRules.MoveNext())
								{
									r = (XmlNode)ienumRules.Current;
									rule = getRule(r);
								}
							}

						}
						else
						{
							ruleTrue = CheckCon(perem, var);
						}
						if (!ruleTrue)
						{
							if (ienumRules.MoveNext())
							{
								r = (XmlNode)ienumRules.Current;
								rule = getRule(r);
							}
							else
							{
								isCompatible = false;
								//MessageBox.Show("Правил, содержащих целевую переменную не найдено. Система несовместна");
								//break;
							}
						}
                        
					}
					if (ruleTrue)
					{
						MessageBox.Show("Найдено решение");
						ShowExplanation(rule);
						lastRule = rule;
						form.просмотрРезультатаToolStripMenuItem.Enabled = true;
						rule = null;
					}

				}
				if (!isCompatible)
				{
					isCompatible = false;
                    MessageBox.Show("Правило не найдено");
				}
				i++;
			}
		}

        public Variable FindPeremByName(string name)
        {
            return variables.Find(x => x.Name == name);
        }

        public bool CheckCon(Variable var1, Variable var2)
        {
            return (Convert.ToBoolean(var1.Value) == Convert.ToBoolean(var2.Value));
        }
        public void ReadBZ()
        {
            XmlNodeList goal =  xmlDoc.GetElementsByTagName("goal");
            XmlNodeList goalList = goal[0].ChildNodes;
            XmlNode name = goalList.Item(0);

            GetAllVariblesInBZ();

           // getRuleByResult("r2");

            BackwardChaining();
        }

		public void ReadBZFC()
		{
			XmlNodeList goal = xmlDoc.GetElementsByTagName("goal");
			XmlNodeList goalList = goal[0].ChildNodes;
			XmlNode name = goalList.Item(0);

			GetAllVariblesInBZ();
			ForwardChaining();
		}

    
        public List<Variable> getVariables(XmlNode variables)
        {
            List<Variable> varList = new List<Variable>(); 
            foreach (XmlAttribute attr in variables.Attributes)
            {
                Variable var = new Variable(attr.Name, attr.Value);
                varList.Add(var);
        
            }
            return varList;
        }

       


        public void getNextRule()
        {
 
        }

        public bool ShowWindow(string question)
        {
            QuestionForm form = new QuestionForm(question);
            form.ShowDialog();
            return form.GetLogicAnswer();
        }

        public void ShowExplanation(Rule rule)
        {
            Explanation fm = new Explanation(rule);

            int y = 15;

            Label activRule = new Label();
            activRule.AutoSize = true;
            activRule.Text = "Было активировано правило: " + rule.Name;
            activRule.Location = new System.Drawing.Point(25, y);
            activRule.Visible = true;
            fm.Controls.Add(activRule);
            y += 20;



            int i = 1;
            foreach (Variable var in rule.Sender)
            {
                Label label = new Label();
                label.AutoSize = true;
                string res = Convert.ToBoolean(FindPeremByName(var.Name).Value) ? "Да": "Нет";
                label.Text = string.Format("{0}.{1}: {2}", i.ToString(), FindPeremByName(var.Name).Comment,res);
                label.Location = new System.Drawing.Point(25, y);
                label.Visible = true;
                fm.Controls.Add(label);
                y += 20;
                i++;

            }
            y += 10;
            Label result = new Label();
            result.AutoSize = true;
            result.Text = "следовательно, рекомендация:";
            result.Location = new System.Drawing.Point(25, y);
            result.Visible = true;
            fm.Controls.Add(result);
            y += 20;

            Label result1 = new Label();
            result1.AutoSize = true;
            result1.Text = rule.Result.Value.ToString();
            result1.Location = new System.Drawing.Point(25, y);
            result1.Visible = true;
            fm.Controls.Add(result1);

            fm.ShowDialog();
            
            
        }





    }
}
